/*
 *  Multithreades parallel processing, single process server.
 *  Best suitated for average duration of session with multiple
 *  I/O intesive processing.
 */

/* Includes */
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include <string.h>
#include<signal.h>
#include<fcntl.h>
#include<netinet/in.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/errno.h>
#include <sys/wait.h>
#include <sys/socket.h>   // For Socket Functions
#include <string.h>       // FOr String FUnctions
#include <pthread.h>      // For Threading FUnctions
#include <errno.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <stdarg.h>
#include "sock.h"
#include "util.h"

#include "server_utils.h"
#include "bid_service.h"

/* Global */

/*
 Note: Enahance to service out of band service request to print resources
 like curr_session_count, high and low session water mark,
 totoal number of sessions served till now, total number of requests rejected,
 start time, last critical error etc.
 */

/* Routine to handle the client request */
void start_dynamic_bid_session(int cSocket, struct sockaddr_in cAddress);
static void destroy_dynamic_session(session_ctx *, int error);
static void* handle_session(void *);




/*
 * Allocate session context and spawn thread for handling session.
 */
void start_dynamic_bid_session(int cSocket, struct sockaddr_in cAddress) {
	static int counter = 0;
	char client_ip[256];

	if (g.curr_sessions_count >= BSD_MAX_SESSION_COUNT) {
		LOG(DEBUG,
				"WARNING: Server reached max capacity... rejecting request for service");
		close(cSocket);
		sleep(BSD_MAX_IDLE_TIME);//Slow down new request intake.
		return;
	}

	g.curr_sessions_count++;
	g.total_sessions_served++;
	g.active_sessions_watermark = g.active_sessions_watermark > g.curr_sessions_count ?
			g.active_sessions_watermark : g.curr_sessions_count;

	session_ctx *ctx = (session_ctx *) malloc(sizeof(session_ctx));

	initialize_context(ctx);

	ctx->sock_fd = cSocket;
	ctx->session_id = counter++;

	client_ip[0] = 0;
	ctx->client_ip = strdup(inet_ntop(AF_INET, &cAddress.sin_addr, client_ip,
			sizeof(client_ip)));

	ctx->client_port = ntohs(cAddress.sin_port);

	printf("Current number of threads %ld \n", g.curr_sessions_count);

	time(&ctx->connect_time);

	/* create thread and delegate the connection processing*/
	pthread_create(&ctx->this, NULL, &handle_session, (void *) ctx);
	//handle_request(ctx); iterative server mode

	return;
}

/**
 * Release resources
 * Destroy thread context.
 */
void destroy_dynamic_session(session_ctx *ctx_ptr, int error) {
	session_ctx *ctx = ctx_ptr;
	time_t end_time;
	double t_diff;

	time(&end_time);
	t_diff = difftime(end_time, ctx->connect_time);
	g.avg_session_duration = g.avg_session_duration +
								((t_diff - g.avg_session_duration)/(g.total_sessions_served));

	if (ctx->eye_catcher != BSD_SESSION_CTX) {
		//Memory corruption or incorrect usage of destroy_session.
		//die...
		LOG(CRITICAL, "Invalid context received in destroy_session");
		shutdown_server(SRVRE_CRITICAL_ERROR,
				"Critical internal error: Invaid eye catcher field");
		return;
	}

	if (ctx->sock_fd > 0) {
		close(ctx->sock_fd);
	}

	if (ctx->client_ip) {
		LOG(DEBUG, "Ending session for client %d, IP: %s", ctx->session_id,
				ctx->client_ip);
		free(ctx->client_ip);
	}

	free(ctx);
	g.curr_sessions_count--;
	printf("Current number of threads %ld \n", g.curr_sessions_count);

	return;
}

/**
 * Read file and write it to the socket.
 **/
void* handle_session(void *ctx_ptr) {
	LOG(DEBUG, "Inside handle_request");

	/* type cast to a pointer to thdata */
	session_ctx *ctx = (session_ctx *) ctx_ptr;

	if (ctx->eye_catcher != BSD_SESSION_CTX) {
		LOG(CRITICAL, "Invalid context received in handle_session");
		shutdown_server(SRVRE_CRITICAL_ERROR,
				"Critical internal error: Invaid eye catcher field");
	}

	//pthread_detach(pthread_self());

	/* do the work */
	printf("Thread %ld \n", (long)ctx->this);
	printf("Session id %d \n", ctx->session_id);

	ctx->curr_state = BSE_ST_CONNECTED;
	ctx->event = BSE_EV_SUCCESS;
	fsm(ctx);

	destroy_dynamic_session(ctx, 0);
	return NULL;

} /* print_message_function ( void *ptr ) */
